import org.identityconnectors.common.security.GuardedString;

import javax.swing.*;

public class PasswordManagerCore {

    VaultFile vaultfile;

    PasswordManagerCore()
    {
//        _nlink = new PasswordManagerCoreNative();
        vaultfile = new VaultFile();
//        cache_accounts = _NL_CACHE_STALE;
//        restore_rv = false;
    }


    public native int vault_open(String path);

    public native int _vault_initialize();

    public int set_master_password(GuardedString masterpass, GuardedString masterpass1) {
        JOptionPane.showMessageDialog(null, "set_master_password unimplemented!!!");
        return 0;
    }

    public int vault_unlock(GuardedString passphrase) {
        int retries = 3;
        int rv = 0;

        // Retry if a power event brings down the Intel SGX enclave
        // (max of 3 retries).

        while (retries-- != 0) {
            rv = _vault_unlock(passphrase);
            if (rv == PasswordManagerError.NL_STATUS_RECREATED_ENCLAVE) {
                // Reinitialize the enclave and vault from the data file and try again
                rv= _vault_reinitialize();
                if (rv != PasswordManagerError.NL_STATUS_OK) return (int) PasswordManagerError.NL_STATUS_LOST_ENCLAVE;
            }
            else return rv;
        }

        return rv;
    }

    String M_GuardedString_to_String(GuardedString ss) {

        class M {
            String s;
        }
        M m = new M();
        //String s="abc";
        ss.access(new GuardedString.Accessor() {
            @Override
            public void access(char[] chars) {
                m.s = new String(chars);
                //s = new String(chars);
                //String t = s;
            }
        });

        return m.s;
    }

    char[] M_GuardedString_to_charArray(GuardedString ss) {
        String s = M_GuardedString_to_String(ss);
        return s.toCharArray();
    }

    int _vault_unlock(GuardedString passphrase) {
        int rv;

        if (vaultfile.is_new()) return (int) PasswordManagerError.NL_STATUS_INVALID;

        if (!vaultfile.is_open()) {
            rv = vaultfile.open_read(vaultfile.get_vault_path());
            if (rv != PasswordManagerError.NL_STATUS_OK) return rv;
        }

//        LPWSTR wpassphrase = M_SecureString_to_LPWSTR(passphrase);
//        if (wpassphrase == NULL) return (int) PasswordManagerError.NL_STATUS_ALLOC;
//
//        rv = _nlink.vault_unlock(wpassphrase);
//        M_Free_LPWSTR(wpassphrase);
        char[] wpassphrase = M_GuardedString_to_charArray(passphrase);
        if (wpassphrase == null) return (int) PasswordManagerError.NL_STATUS_ALLOC;

        rv = _vault_unlock_native(wpassphrase);

        if (rv != PasswordManagerError.NL_STATUS_OK) return rv;

        return send_vault_data();
    }

    private native int _vault_unlock_native(char[] wpassphrase);


    int send_vault_data()
    {
//        PBYTE edata;
        int rv, vault_size;
        byte evaultdata[];

        vault_size = (int) vaultfile.get_vault_size();

        evaultdata = new byte[vault_size];
        rv = vaultfile.get_vault(evaultdata);
        if (rv != PasswordManagerError.NL_STATUS_OK) return rv;

        // Send this to the vault for decrypting


//
//        rv= _nlink->load_vault(edata);
//
//        delete[] edata;

        rv = send_vault_data_native(evaultdata, vault_size);

        return rv;
    }

    public native int send_vault_data_native(byte[] evaultdata, int vault_size);


    int _vault_reinitialize() {
        if (! vaultfile.is_open()) return (int) PasswordManagerError.NL_STATUS_INVALID;

        return this._vault_initialize();
    }

    public int accounts_get_count(BoxInt boxCount) {
        //int c;
        int rv = 0;
        int tries = 3;

        while (tries-- != 0) {
            //rv = _nlink->accounts_get_count(&c);
            rv = accounts_get_count_native(boxCount);
            if (rv == PasswordManagerError.NL_STATUS_RECREATED_ENCLAVE) {
                if ( ! restore_vault() ) {
                    rv = (int) PasswordManagerError.NL_STATUS_LOST_ENCLAVE;
                    tries = 0;
                }
            }
            else break;
        }

        if (rv != PasswordManagerError.NL_STATUS_OK) return rv;

        //boxCount.theInt = c;

        return (int) PasswordManagerError.NL_STATUS_OK;
    }

    private native int accounts_get_count_native(BoxInt boxCount);

    boolean restore_vault() { return restore_vault(false); }

    boolean restore_vault(boolean flag_async)
    {
        return true;
//        bool got_lock= false;
//        int rv;
//
//        // Only let one thread do the restore if both come in at the
//        // same time. A spinlock approach is inefficient but simple.
//        // This is OK for our application, but a high-performance
//        // application (or one with a long-running work loop)
//        // would want something else.
//
//        try {
//            slock.Enter(got_lock);
//
//            if (_nlink->supports_sgx()) {
//                bool do_restore = true;
//
//                // This part is only needed for enclave-based vaults.
//
//                if (flag_async) {
//                    // If we are entering as a result of a power event,
//                    // make sure the vault has not already been restored
//                    // by the synchronous/UI thread (ie, a failed ECALL).
//
//                    rv = _nlink->ping_vault();
//                    if (rv != NL_STATUS_LOST_ENCLAVE) do_restore = false;
//                    // If do_store is false, then we'll also use the
//                    // last value of rv_restore as our return value.
//                    // This will tell us whether or not we should lock the
//                    // vault.
//                }
//
//                if (do_restore) {
//                    // If the vaultfile isn't open then we are locked or hadn't
//                    // been opened to be begin with.
//
//                    if (!vaultfile->is_open()) {
//                        // Have we opened a vault yet?
//                        if (vaultfile->get_vault_path()->Length == 0) goto restore_error;
//
//                        // We were explicitly locked, so reopen.
//                        rv = vaultfile->open_read(vaultfile->get_vault_path());
//                        if (rv != NL_STATUS_OK) goto restore_error;
//                    }
//
//                    // Reinitialize the vault from the header.
//
//                    rv = _vault_reinitialize();
//                    if (rv != NL_STATUS_OK) goto restore_error;
//
//                    // Now, call to the native object to restore the vault state.
//                    rv = _nlink->restore_vault_state();
//                    if (rv != NL_STATUS_OK) goto restore_error;
//
//                    // The database password was restored to the vault. Now restore
//                    // the vault, itself.
//
//                    rv = send_vault_data();
//                    restore_error:
//                    restore_rv = (rv == NL_STATUS_OK);
//                }
//            }
//            else {
//                rv = _nlink->check_vault_state();
//                restore_rv = (rv == NL_STATUS_OK);
//            }
//
//            slock.Exit(false);
//        }
//        catch (...) {
//        // We don't need to do anything here.
//    }
//
//        return restore_rv;
    }


    public int accounts_get_info(int idx, BoxGuardedString boxSsname, BoxGuardedString boxLogin, BoxGuardedString boxUrl) {
//        JOptionPane.showMessageDialog(null, "accounts_get_info unimplemented!!!");
        int rv = 0;
        //UINT32 index = idx;
        //wchar_t *wname, *wlogin, *wurl;
        int tries = 3;

        BoxCharArray boxWname = new BoxCharArray(null);
        BoxCharArray boxWlogin = new BoxCharArray(null);
        BoxCharArray boxWurl = new BoxCharArray(null);

        while (tries-- != 0) {
            //rv = _nlink->accounts_get_info(index, &wname, &wlogin, &wurl);

            rv = accounts_get_info_native(idx, boxWname, boxWlogin, boxWurl);
            if (rv == PasswordManagerError.NL_STATUS_RECREATED_ENCLAVE) {
                if (!restore_vault()) {
                    rv = (int) PasswordManagerError.NL_STATUS_LOST_ENCLAVE;
                    tries = 0;
                }
            }
            else break;
        }

        if (rv == PasswordManagerError.NL_STATUS_OK) {
            try {
                boxSsname.theString = new GuardedString(boxWname.theArray);
                boxLogin.theString = new GuardedString(boxWlogin.theArray);
                boxUrl.theString = new GuardedString(boxWurl.theArray);
            }
            catch (Exception e) {
                rv = (int) PasswordManagerError.NL_STATUS_ALLOC;
            }

            //_nlink->accounts_release_info(wname, wlogin, wurl);
            // 参数通过.cpp中的全局变量传递了
            accounts_release_info_native();
        }

        return rv;
    }

    private native void accounts_release_info_native();

    private native int accounts_get_info_native(int index, BoxCharArray boxWname, BoxCharArray boxWlogin, BoxCharArray boxWurl);
}
